package com.tangsm.spring.boot.strategy.demo.factory;

import com.tangsm.spring.boot.strategy.demo.annotation.MessageTransports;
import com.tangsm.spring.boot.strategy.demo.base.SendMessage;
import com.tangsm.spring.boot.strategy.demo.enums.MessageTypeEnum;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 获取消息策略实现实例方式
 *
 * <p>获取应用程序上下文：ApplicationContextAware.setApplicationContext</p>
 * <p>@PostConstruct：依赖注入完成后执行任何初始化的方法，用于获取所有策略bean实例</p>
 * <p>获取bean实例：context.getBeansOfType</p>
 */
@Component
public class SendMessageContext implements ApplicationContextAware {

    protected ApplicationContext context;

    /**
     * 使用线程安全的ConcurrentHashMap
     */
    private final Map<String, SendMessage> mapping = new ConcurrentHashMap<>();

    /**
     * 初始化，获取所有实现SendMessageService接口MessageTransports注解容器实例
     * <p>@PostConstruct声明在创建完成对象并且注入属性后执行改方法</p>
     */
    @PostConstruct
    protected void init() {
        // 获取容器中所有实现SendMessageService接口都实例
        Map<String, SendMessage> messageMap = context.getBeansOfType(SendMessage.class);

        // 过滤非使用MessageTransports的实例
        if (messageMap.size() > 0) {

            // 遍历所有实例过滤没有MessageTransports注解的实例
            for (SendMessage impl : messageMap.values()) {
                // 判断类上是否存在MessageTransports注释
                if (!impl.getClass().isAnnotationPresent(MessageTransports.class)) {
                    continue;
                }

                // 获取MessageTransports注释对象
                MessageTransports transport = impl.getClass().getAnnotation(MessageTransports.class);
                // 获取注解类型枚举
                MessageTypeEnum type = transport.value();

                mapping.put(type.getCode(), impl);
            }
        }
    }

    /**
     * 获取实现策略类
     * @param key 消息运输类型
     * @return 策略类
     */
    public SendMessage getInstance(String key) {
        return mapping.get(key);
    }

    @SuppressWarnings("NullableProblems")
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}
